New public API to save pixbufs to non-file locations. (#82203, Tim Evans)
authorMatthias Clasen <maclas@gmx.de>
Wed, 7 Jan 2004 01:57:42 +0000 (01:57 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Wed, 7 Jan 2004 01:57:42 +0000 (01:57 +0000)
Wed Jan  7 02:41:14 2004  Matthias Clasen  <maclas@gmx.de>

* gdk-pixbuf-io.c (gdk_pixbuf_save_to_bufferv):
* gdk-pixbuf-io.c (gdk_pixbuf_save_to_buffer):
* gdk-pixbuf-io.c (gdk_pixbuf_save_to_callbackv):
* gdk-pixbuf-io.c (gdk_pixbuf_save_to_callback): New public API
to save pixbufs to non-file locations.  (#82203, Tim Evans)

* gdk-pixbuf.h:
* gdk_pixbuf.def: Declare the new public API.

* gdk-pixbuf-io.h: Add save_to_callback to the module interface.

* io-jpeg.c:
* io-png.c: Implement save_to_callback.

* gdk-pixbuf-io.c (gdk_pixbuf_savev):
* gdk-pixbuf-io.c (gdk_pixbuf_save): Update documentation.

* gdk-pixbuf-io.c (gdk_pixbuf_real_save): Support saving via
image_module->save_to_callback.

* gdk-pixbuf-io.c (gdk_pixbuf_real_save_to_callback): New generic
save_to_callback function which falls back to image_module->save
on a temp file.

gdk-pixbuf/ChangeLog
gdk-pixbuf/gdk-pixbuf-io.c
gdk-pixbuf/gdk-pixbuf-io.h
gdk-pixbuf/gdk-pixbuf-loader.c
gdk-pixbuf/gdk-pixbuf.h
gdk-pixbuf/gdk_pixbuf.def
gdk-pixbuf/io-jpeg.c
gdk-pixbuf/io-png.c

index 796b17833cba143badb2783b6d91bd43d1e6f56e..c09fdd5c4705cdf546a1f5032f4018fb22dc33a1 100644 (file)
@@ -1,3 +1,29 @@
+Wed Jan  7 02:41:14 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * gdk-pixbuf-io.c (gdk_pixbuf_save_to_bufferv): 
+       * gdk-pixbuf-io.c (gdk_pixbuf_save_to_buffer): 
+       * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callbackv): 
+       * gdk-pixbuf-io.c (gdk_pixbuf_save_to_callback): New public API
+       to save pixbufs to non-file locations.  (#82203, Tim Evans)
+
+       * gdk-pixbuf.h:
+       * gdk-pixbuf.def: Declare the new public API.
+
+       * gdk-pixbuf-io.h: Add save_to_callback to the module interface.
+       
+       * io-jpeg.c: 
+       * io-png.c: Implement save_to_callback.
+
+       * gdk-pixbuf-io.c (gdk_pixbuf_savev): 
+       * gdk-pixbuf-io.c (gdk_pixbuf_save): Update documentation. 
+
+       * gdk-pixbuf-io.c (gdk_pixbuf_real_save): Support saving via
+       image_module->save_to_callback.
+
+       * gdk-pixbuf-io.c (gdk_pixbuf_real_save_to_callback): New generic
+       save_to_callback function which falls back to image_module->save 
+       on a temp file.
+       
 Wed Jan  7 01:17:36 2004  Matthias Clasen  <maclas@gmx.de>
 
        * gdk-pixbuf-loader.c (gdk_pixbuf_loader_size_func): 
index dc5dc51431f422cc38cb46e659dd6cd948e95845..e67564c223aa23c04c167bcc98a9725f112eff7d 100644 (file)
@@ -1073,6 +1073,27 @@ collect_save_options (va_list   opts,
     }
 }
 
+static gboolean
+save_to_file_callback (const gchar *buf,
+                      gsize        count,
+                      GError     **error,
+                      gpointer     data)
+{
+       FILE *filehandle = data;
+       gsize n;
+
+       n = fwrite (buf, 1, count, filehandle);
+       if (n != count) {
+                g_set_error (error,
+                             G_FILE_ERROR,
+                             g_file_error_from_errno (errno),
+                             _("Error writing to image file: %s"),
+                             g_strerror (errno));
+                return FALSE;
+       }
+       return TRUE;
+}
+
 static gboolean
 gdk_pixbuf_real_save (GdkPixbuf     *pixbuf, 
                       FILE          *filehandle, 
@@ -1092,7 +1113,137 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
                if (!_gdk_pixbuf_load_module (image_module, error))
                        return FALSE;
 
-       if (image_module->save == NULL) {
+       if (image_module->save) {
+              /* save normally */
+              return (* image_module->save) (filehandle, pixbuf,
+                                             keys, values,
+                                             error);
+       }
+       else if (image_module->save_to_callback) {
+              /* save with simple callback */
+              return (* image_module->save_to_callback) (save_to_file_callback,
+                                                         filehandle, pixbuf,
+                                                         keys, values,
+                                                         error);
+       }
+       else {
+              /* can't save */
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+                            _("This build of gdk-pixbuf does not support saving the image format: %s"),
+                            type);
+               return FALSE;
+       }
+}
+
+#define TMP_FILE_BUF_SIZE 4096
+
+static gboolean
+save_to_callback_with_tmp_file (GdkPixbufModule   *image_module,
+                               GdkPixbuf         *pixbuf,
+                               GdkPixbufSaveFunc  save_func,
+                               gpointer           user_data,
+                               gchar            **keys,
+                               gchar            **values,
+                               GError           **error)
+{
+       int fd;
+       FILE *f = NULL;
+       gboolean retval = FALSE;
+       gchar *buf = NULL;
+       gsize n;
+       gchar *filename = NULL;
+
+       buf = g_try_malloc (TMP_FILE_BUF_SIZE);
+       if (buf == NULL) {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                            _("Insufficient memory to save image to callback"));
+               goto end;
+       }
+
+       fd = g_file_open_tmp ("gdkpixbuf-save-tmp.XXXXXX", &filename, error);
+       if (fd == -1)
+               goto end;
+       f = fdopen (fd, "wb+");
+       if (f == NULL) {
+               g_set_error (error,
+                            G_FILE_ERROR,
+                            g_file_error_from_errno (errno),
+                            _("Failed to open temporary file"));
+               goto end;
+       }
+       if (!(* image_module->save) (f, pixbuf, keys, values, error))
+               goto end;
+       rewind (f);
+       for (;;) {
+               n = fread (buf, 1, TMP_FILE_BUF_SIZE, f);
+               if (n > 0) {
+                       if (!save_func (buf, n, error, user_data))
+                               goto end;
+               }
+               if (n != TMP_FILE_BUF_SIZE) 
+                       break;
+       }
+       if (ferror (f)) {
+               g_set_error (error,
+                            G_FILE_ERROR,
+                            g_file_error_from_errno (errno),
+                            _("Failed to read from temporary file"));
+               goto end;
+       }
+       retval = TRUE;
+
+ end:
+       /* cleanup and return retval */
+       if (f)
+               fclose (f);
+       if (filename) {
+               unlink (filename);
+               g_free (filename);
+       }
+       g_free (buf);
+
+       return retval;
+}
+
+static gboolean
+gdk_pixbuf_real_save_to_callback (GdkPixbuf         *pixbuf,
+                                 GdkPixbufSaveFunc  save_func,
+                                 gpointer           user_data,
+                                 const char        *type, 
+                                 gchar            **keys,
+                                 gchar            **values,
+                                 GError           **error)
+{
+       GdkPixbufModule *image_module = NULL;       
+
+       image_module = _gdk_pixbuf_get_named_module (type, error);
+
+       if (image_module == NULL)
+               return FALSE;
+       
+       if (image_module->module == NULL)
+               if (!_gdk_pixbuf_load_module (image_module, error))
+                       return FALSE;
+
+       if (image_module->save_to_callback) {
+              /* save normally */
+              return (* image_module->save_to_callback) (save_func, user_data, 
+                                                         pixbuf, keys, values,
+                                                         error);
+       }
+       else if (image_module->save) {
+              /* use a temporary file */
+              return save_to_callback_with_tmp_file (image_module, pixbuf,
+                                                     save_func, user_data, 
+                                                     keys, values,
+                                                     error);
+       }
+       else {
+              /* can't save */
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
@@ -1100,10 +1251,6 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
                             type);
                return FALSE;
        }
-               
-       return (* image_module->save) (filehandle, pixbuf,
-                                      keys, values,
-                                      error);
 }
 
  
@@ -1115,8 +1262,25 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
  * @error: return location for error, or %NULL
  * @Varargs: list of key-value save options
  *
- * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or
- * "ico".  If @error is set, %FALSE will be returned. Possible errors include 
+ * Saves pixbuf to a file in format @type. By default, "jpeg", "png" and 
+ * "ico" are possible file formats to save in, but more formats may be
+ * installed. The list of all writable formats can be determined in the 
+ * following way:
+ *
+ * <informalexample><programlisting>
+ * void add_if_writable (GdkPixbufFormat *data, GSList **list)
+ * {
+ *   if (gdk_pixbuf_format_is_writable (data))
+ *     *list = g_slist_prepend (*list, data);
+ * }
+ *
+ * GSList *formats = gdk_pixbuf_get_formats (<!-- -->);
+ * GSList *writable_formats = NULL;
+ * g_slist_foreach (formats, add_if_writable, &writable_formats);
+ * g_slist_free (formats);
+ * </programlisting></informalexample>
+ *
+ * If @error is set, %FALSE will be returned. Possible errors include 
  * those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
  *
  * The variable argument list should be %NULL-terminated; if not empty,
@@ -1178,9 +1342,9 @@ gdk_pixbuf_save (GdkPixbuf  *pixbuf,
  * @option_values: values for named options
  * @error: return location for error, or %NULL
  *
- * Saves pixbuf to a file in @type, which is currently "jpeg" or "png".
- * If @error is set, %FALSE will be returned. See gdk_pixbuf_save () for more
- * details.
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or "ico".
+ * If @error is set, %FALSE will be returned. 
+ * See gdk_pixbuf_save () for more details.
  *
  * Return value: whether an error was set
  **/
@@ -1236,6 +1400,251 @@ gdk_pixbuf_savev (GdkPixbuf  *pixbuf,
        return TRUE;
 }
 
+/**
+ * gdk_pixbuf_save_to_callback:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ *   the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf in format @type by feeding the produced data to a 
+ * callback. Can be used when you want to store the image to something 
+ * other than a file, such as an in-memory buffer or a socket.  
+ * If @error is set, %FALSE will be returned. Possible errors
+ * include those in the #GDK_PIXBUF_ERROR domain and whatever the save
+ * function generates.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callback    (GdkPixbuf  *pixbuf,
+                               GdkPixbufSaveFunc save_func,
+                               gpointer user_data,
+                               const char *type, 
+                               GError    **error,
+                               ...)
+{
+        gchar **keys = NULL;
+        gchar **values = NULL;
+        va_list args;
+        gboolean result;
+        
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+        
+        va_start (args, error);
+        
+        collect_save_options (args, &keys, &values);
+        
+        va_end (args);
+
+        result = gdk_pixbuf_save_to_callbackv (pixbuf, save_func, user_data,
+                                              type, keys, values,
+                                              error);
+
+        g_strfreev (keys);
+        g_strfreev (values);
+
+        return result;
+}
+
+/**
+ * gdk_pixbuf_save_to_callbackv:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ *   the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: name of file format.
+ * @option_keys: name of options to set, %NULL-terminated
+ * @option_values: values for named options
+ * @error: return location for error, or %NULL
+ *
+ * Saves pixbuf to a callback in format @type, which is currently "jpeg",
+ * "png" or "ico".  If @error is set, %FALSE will be returned. See
+ * gdk_pixbuf_save_to_callback () for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callbackv   (GdkPixbuf  *pixbuf, 
+                               GdkPixbufSaveFunc save_func,
+                               gpointer user_data,
+                               const char *type,
+                               char      **option_keys,
+                               char      **option_values,
+                               GError    **error)
+{
+        gboolean result;
+        
+       
+        g_return_val_if_fail (save_func != NULL, FALSE);
+        g_return_val_if_fail (type != NULL, FALSE);
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+       
+       result = gdk_pixbuf_real_save_to_callback (pixbuf,
+                                                 save_func, user_data, type,
+                                                 option_keys, option_values,
+                                                 error);
+       
+       if (!result) {
+               g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_buffer:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
+ * "png" or "ico".  This is a convenience function that uses
+ * gdk_pixbuf_save_to_callback() to do the real work. Note that the buffer 
+ * is not nul-terminated and may contain embedded  nuls.
+ * If @error is set, %FALSE will be returned and @string will be set to
+ * %NULL. Possible errors include those in the #GDK_PIXBUF_ERROR
+ * domain.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_buffer      (GdkPixbuf  *pixbuf,
+                               gchar     **buffer,
+                               gsize      *buffer_size,
+                               const char *type, 
+                               GError    **error,
+                               ...)
+{
+        gchar **keys = NULL;
+        gchar **values = NULL;
+        va_list args;
+        gboolean result;
+        
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+        
+        va_start (args, error);
+        
+        collect_save_options (args, &keys, &values);
+        
+        va_end (args);
+
+        result = gdk_pixbuf_save_to_bufferv (pixbuf, buffer, buffer_size,
+                                            type, keys, values,
+                                            error);
+
+        g_strfreev (keys);
+        g_strfreev (values);
+
+        return result;
+}
+
+struct SaveToBufferData {
+       gchar *buffer;
+       gsize len, max;
+};
+
+static gboolean
+save_to_buffer_callback (const gchar *data,
+                        gsize count,
+                        GError **error,
+                        gpointer user_data)
+{
+       struct SaveToBufferData *sdata = user_data;
+       gchar *new_buffer;
+       gsize new_max;
+
+       if (sdata->len + count > sdata->max) {
+               new_max = MAX (sdata->max*2, sdata->len + count);
+               new_buffer = g_try_realloc (sdata->buffer, new_max);
+               if (!new_buffer) {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                    _("Insufficient memory to save image into a buffer"));
+                       return FALSE;
+               }
+               sdata->buffer = new_buffer;
+               sdata->max = new_max;
+       }
+       memcpy (sdata->buffer + sdata->len, data, count);
+       sdata->len += count;
+       return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_bufferv:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: name of file format.
+ * @option_keys: name of options to set, %NULL-terminated
+ * @option_values: values for named options
+ * @error: return location for error, or %NULL
+ *
+ * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
+ * "png" or "ico".  See gdk_pixbuf_save_to_buffer() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_bufferv     (GdkPixbuf  *pixbuf,
+                               gchar     **buffer,
+                               gsize      *buffer_size,
+                               const char *type, 
+                               char      **option_keys,
+                               char      **option_values,
+                               GError    **error)
+{
+       static const gint initial_max = 1024;
+       struct SaveToBufferData sdata;
+
+       *buffer = NULL;
+       *buffer_size = 0;
+
+       sdata.buffer = g_try_malloc (initial_max);
+       sdata.max = initial_max;
+       sdata.len = 0;
+       if (!sdata.buffer) {
+                g_set_error (error,
+                             GDK_PIXBUF_ERROR,
+                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                            _("Insufficient memory to save image into a buffer"));
+               return FALSE;
+       }
+
+       if (!gdk_pixbuf_save_to_callbackv (pixbuf,
+                                          save_to_buffer_callback, &sdata,
+                                          type, option_keys, option_values,
+                                          error)) {
+               g_free (sdata.buffer);
+               return FALSE;
+       }
+
+       *buffer = sdata.buffer;
+       *buffer_size = sdata.len;
+       return TRUE;
+}
+
 /**
  * gdk_pixbuf_format_get_name:
  * @format: a #GdkPixbufFormat
index d62669af200caeff895b8e2a3e56f7ac4727e387..b4fadce99b82405d983faab9bbaf9e26bf67aa9a 100644 (file)
@@ -89,11 +89,19 @@ struct _GdkPixbufModule {
        GdkPixbufAnimation *(* load_animation) (FILE    *f,
                                                 GError **error);
 
+        /* Saving */
         gboolean (* save) (FILE      *f,
                            GdkPixbuf *pixbuf,
                            gchar    **param_keys,
                            gchar    **param_values,
                            GError   **error);
+
+        gboolean (*save_to_callback) (GdkPixbufSaveFunc save_func,
+                                     gpointer user_data,
+                                     GdkPixbuf *pixbuf,
+                                     gchar **option_keys,
+                                     gchar **option_values,
+                                     GError **error);
   
   /*< private >*/
        void (*_reserved1) (void); 
@@ -101,7 +109,6 @@ struct _GdkPixbufModule {
        void (*_reserved3) (void); 
        void (*_reserved4) (void); 
        void (*_reserved5) (void); 
-       void (*_reserved6) (void); 
 
 };
 
@@ -116,7 +123,7 @@ gboolean gdk_pixbuf_set_option  (GdkPixbuf   *pixbuf,
 
 typedef enum /*< skip >*/
 {
-  GDK_PIXBUF_FORMAT_WRITABLE = 1 << 0
+  GDK_PIXBUF_FORMAT_WRITABLE = 1 << 0,
 } GdkPixbufFormatFlags;
 
 struct _GdkPixbufFormat {
index d980eea97bb88a6248d3bfd6428f70b3311c3af7..54eff3280df4218c195039346152ebc1f54bf7d2 100644 (file)
@@ -225,7 +225,6 @@ gdk_pixbuf_loader_finalize (GObject *object)
 
         if (!priv->closed)
                 g_warning ("GdkPixbufLoader finalized without calling gdk_pixbuf_loader_close() - this is not allowed. You must explicitly end the data stream to the loader before dropping the last reference.");
-  
         if (priv->animation)
                 g_object_unref (priv->animation);
   
index 234f88310e26f78349457541d2214b83f67d2a4c..a4054cc078f4485c2cae121d94e51deb83e7e771 100644 (file)
@@ -170,6 +170,45 @@ gboolean gdk_pixbuf_savev          (GdkPixbuf  *pixbuf,
                                     char      **option_values,
                                     GError    **error);
 
+/* Saving to a callback function */
+
+typedef gboolean (*GdkPixbufSaveFunc)   (const gchar *buf,
+                                        gsize count,
+                                        GError **error,
+                                        gpointer data);
+
+gboolean gdk_pixbuf_save_to_callback    (GdkPixbuf  *pixbuf,
+                                        GdkPixbufSaveFunc save_func,
+                                        gpointer user_data,
+                                        const char *type, 
+                                        GError    **error,
+                                        ...);
+
+gboolean gdk_pixbuf_save_to_callbackv   (GdkPixbuf  *pixbuf, 
+                                        GdkPixbufSaveFunc save_func,
+                                        gpointer user_data,
+                                        const char *type,
+                                        char      **option_keys,
+                                        char      **option_values,
+                                        GError    **error);
+
+/* Saving into a newly allocated char array */
+
+gboolean gdk_pixbuf_save_to_buffer      (GdkPixbuf  *pixbuf,
+                                        gchar     **buffer,
+                                        gsize      *buffer_size,
+                                        const char *type, 
+                                        GError    **error,
+                                        ...);
+
+gboolean gdk_pixbuf_save_to_bufferv     (GdkPixbuf  *pixbuf,
+                                        gchar     **buffer,
+                                        gsize      *buffer_size,
+                                        const char *type, 
+                                        char      **option_keys,
+                                        char      **option_values,
+                                        GError    **error);
+
 /* Adding an alpha channel */
 GdkPixbuf *gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf, gboolean substitute_color,
                                 guchar r, guchar g, guchar b);
index c7cd424649de087967d84baa332d82d788a44a56..c5042f94f332baffc762555f7f3eb8476f7fc888 100644 (file)
@@ -33,6 +33,7 @@ EXPORTS
        gdk_pixbuf_from_pixdata
        gdk_pixbuf_get_bits_per_sample
        gdk_pixbuf_get_colorspace
+       gdk_pixbuf_get_file_info
        gdk_pixbuf_get_formats
        gdk_pixbuf_get_has_alpha
        gdk_pixbuf_get_height
@@ -60,6 +61,10 @@ EXPORTS
        gdk_pixbuf_ref
        gdk_pixbuf_saturate_and_pixelate
        gdk_pixbuf_save
+       gdk_pixbuf_save_to_callback
+       gdk_pixbuf_save_to_callbackv
+       gdk_pixbuf_save_to_buffer
+       gdk_pixbuf_save_to_bufferv
        gdk_pixbuf_savev
        gdk_pixbuf_scale
        gdk_pixbuf_scale_simple
index 85d75dfe60be264603b8b90e73ebbe8a1f700e48..4dd5f8f2a52f43de756a2d71c3a159b0f76981c6 100644 (file)
@@ -780,12 +780,85 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data,
        return TRUE;
 }
 
+/* Save */
+
+#define TO_FUNCTION_BUF_SIZE 4096
+
+typedef struct {
+       struct jpeg_destination_mgr pub;
+       JOCTET             *buffer;
+       GdkPixbufSaveFunc   save_func;
+       gpointer            user_data;
+       GError            **error;
+} ToFunctionDestinationManager;
+
+void
+to_callback_init (j_compress_ptr cinfo)
+{
+       ToFunctionDestinationManager *destmgr;
+
+       destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+       destmgr->pub.next_output_byte = destmgr->buffer;
+       destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
+}
+
+static void
+to_callback_do_write (j_compress_ptr cinfo, gsize length)
+{
+       ToFunctionDestinationManager *destmgr;
+
+       destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+        if (!destmgr->save_func (destmgr->buffer,
+                                length,
+                                destmgr->error,
+                                destmgr->user_data)) {
+               struct error_handler_data *errmgr;
+        
+               errmgr = (struct error_handler_data *) cinfo->err;
+               /* Use a default error message if the callback didn't set one,
+                * which it should have.
+                */
+               if (errmgr->error && *errmgr->error == NULL) {
+                       g_set_error (errmgr->error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                    "write function failed");
+               }
+               siglongjmp (errmgr->setjmp_buffer, 1);
+               g_assert_not_reached ();
+        }
+}
+
+static guchar
+to_callback_empty_output_buffer (j_compress_ptr cinfo)
+{
+       ToFunctionDestinationManager *destmgr;
+
+       destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+       to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE);
+       destmgr->pub.next_output_byte = destmgr->buffer;
+       destmgr->pub.free_in_buffer = TO_FUNCTION_BUF_SIZE;
+       return TRUE;
+}
+
+void
+to_callback_terminate (j_compress_ptr cinfo)
+{
+       ToFunctionDestinationManager *destmgr;
+
+       destmgr = (ToFunctionDestinationManager*) cinfo->dest;
+       to_callback_do_write (cinfo, TO_FUNCTION_BUF_SIZE - destmgr->pub.free_in_buffer);
+}
+
 static gboolean
-gdk_pixbuf__jpeg_image_save (FILE          *f, 
-                             GdkPixbuf     *pixbuf, 
-                             gchar        **keys,
-                             gchar        **values,
-                             GError       **error)
+real_save_jpeg (GdkPixbuf          *pixbuf,
+               gchar             **keys,
+               gchar             **values,
+               GError            **error,
+               gboolean            to_callback,
+               FILE               *f,
+               GdkPixbufSaveFunc   save_func,
+               gpointer            user_data)
 {
         /* FIXME error handling is broken */
         
@@ -800,6 +873,9 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
        int w, h = 0;
        int rowstride = 0;
        struct error_handler_data jerr;
+       ToFunctionDestinationManager to_callback_destmgr;
+
+       to_callback_destmgr.buffer = NULL;
 
        if (keys && *keys) {
                gchar **kiter = keys;
@@ -854,7 +930,9 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
        pixels = gdk_pixbuf_get_pixels (pixbuf);
        g_return_val_if_fail (pixels != NULL, FALSE);
 
-       /* allocate a small buffer to convert image data */
+       /* Allocate a small buffer to convert image data,
+       * and a larger buffer if doing to_callback save.
+       */
        buf = g_try_malloc (w * 3 * sizeof (guchar));
        if (!buf) {
               g_set_error (error,
@@ -863,6 +941,16 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
                            _("Couldn't allocate memory for loading JPEG file"));
               return FALSE;
        }
+       if (to_callback) {
+              to_callback_destmgr.buffer = g_try_malloc (TO_FUNCTION_BUF_SIZE);
+              if (!to_callback_destmgr.buffer) {
+                      g_set_error (error,
+                                   GDK_PIXBUF_ERROR,
+                                   GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                   _("Couldn't allocate memory for loading JPEG file"));
+                      return FALSE;
+              }
+       }
 
        /* set up error handling */
        jerr.pub.error_exit = fatal_error_handler;
@@ -873,12 +961,23 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
        if (sigsetjmp (jerr.setjmp_buffer, 1)) {
                jpeg_destroy_compress (&cinfo);
                g_free (buf);
+              g_free (to_callback_destmgr.buffer);
                return FALSE;
        }
 
        /* setup compress params */
        jpeg_create_compress (&cinfo);
-       jpeg_stdio_dest (&cinfo, f);
+       if (to_callback) {
+              to_callback_destmgr.pub.init_destination    = to_callback_init;
+              to_callback_destmgr.pub.empty_output_buffer = to_callback_empty_output_buffer;
+              to_callback_destmgr.pub.term_destination    = to_callback_terminate;
+              to_callback_destmgr.error = error;
+              to_callback_destmgr.save_func = save_func;
+              to_callback_destmgr.user_data = user_data;
+              cinfo.dest = (struct jpeg_destination_mgr*) &to_callback_destmgr;
+       } else {
+              jpeg_stdio_dest (&cinfo, f);
+       }
        cinfo.image_width      = w;
        cinfo.image_height     = h;
        cinfo.input_components = 3; 
@@ -909,9 +1008,33 @@ gdk_pixbuf__jpeg_image_save (FILE          *f,
        jpeg_finish_compress (&cinfo);
        jpeg_destroy_compress(&cinfo);
        g_free (buf);
+       g_free (to_callback_destmgr.buffer);
        return TRUE;
 }
 
+static gboolean
+gdk_pixbuf__jpeg_image_save (FILE          *f, 
+                             GdkPixbuf     *pixbuf, 
+                             gchar        **keys,
+                             gchar        **values,
+                             GError       **error)
+{
+       return real_save_jpeg (pixbuf, keys, values, error,
+                              FALSE, f, NULL, NULL);
+}
+
+static gboolean
+gdk_pixbuf__jpeg_image_save_to_callback (GdkPixbufSaveFunc   save_func,
+                                        gpointer            user_data,
+                                        GdkPixbuf          *pixbuf, 
+                                        gchar             **keys,
+                                        gchar             **values,
+                                        GError            **error)
+{
+       return real_save_jpeg (pixbuf, keys, values, error,
+                              TRUE, NULL, save_func, user_data);
+}
+
 void
 MODULE_ENTRY (jpeg, fill_vtable) (GdkPixbufModule *module)
 {
@@ -920,6 +1043,7 @@ MODULE_ENTRY (jpeg, fill_vtable) (GdkPixbufModule *module)
        module->stop_load = gdk_pixbuf__jpeg_image_stop_load;
        module->load_increment = gdk_pixbuf__jpeg_image_load_increment;
        module->save = gdk_pixbuf__jpeg_image_save;
+       module->save_to_callback = gdk_pixbuf__jpeg_image_save_to_callback;
 }
 
 void
index fa35734a7679d1b8c26a75acd70de494ef2fc721..669cb7bfc541f71dfe8000791c9c97f97fc37bd1 100644 (file)
@@ -741,12 +741,40 @@ png_warning_callback(png_structp png_read_ptr,
 
 /* Save */
 
-static gboolean
-gdk_pixbuf__png_image_save (FILE          *f, 
-                            GdkPixbuf     *pixbuf, 
-                            gchar        **keys,
-                            gchar        **values,
-                            GError       **error)
+typedef struct {
+        GdkPixbufSaveFunc save_func;
+        gpointer user_data;
+        GError **error;
+} SaveToFunctionIoPtr;
+
+static void
+png_save_to_callback_write_func (png_structp png_ptr,
+                                 png_bytep   data,
+                                 png_size_t  length)
+{
+        SaveToFunctionIoPtr *ioptr = png_get_io_ptr (png_ptr);
+
+        if (!ioptr->save_func (data, length, ioptr->error, ioptr->user_data)) {
+                /* If save_func has already set an error, which it
+                   should have done, this won't overwrite it. */
+                png_error (png_ptr, "write function failed");
+        }
+}
+
+static void
+png_save_to_callback_flush_func (png_structp png_ptr)
+{
+        ;
+}
+
+static gboolean real_save_png (GdkPixbuf        *pixbuf, 
+                               gchar           **keys,
+                               gchar           **values,
+                               GError          **error,
+                               gboolean          to_callback,
+                               FILE             *f,
+                               GdkPixbufSaveFunc save_func,
+                               gpointer          user_data)
 {
        png_structp png_ptr;
        png_infop info_ptr;
@@ -762,6 +790,7 @@ gdk_pixbuf__png_image_save (FILE          *f,
        int bpc;
        int num_keys;
        gboolean success = TRUE;
+       SaveToFunctionIoPtr to_callback_ioptr;
 
        num_keys = 0;
 
@@ -860,7 +889,16 @@ gdk_pixbuf__png_image_save (FILE          *f,
                png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
        }
 
-       png_init_io (png_ptr, f);
+       if (to_callback) {
+               to_callback_ioptr.save_func = save_func;
+               to_callback_ioptr.user_data = user_data;
+               to_callback_ioptr.error = error;
+               png_set_write_fn (png_ptr, &to_callback_ioptr,
+                                 png_save_to_callback_write_func,
+                                 png_save_to_callback_flush_func);
+       } else {
+               png_init_io (png_ptr, f);
+       }
 
        if (has_alpha) {
                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
@@ -901,6 +939,29 @@ cleanup:
        return success;
 }
 
+static gboolean
+gdk_pixbuf__png_image_save (FILE          *f, 
+                            GdkPixbuf     *pixbuf, 
+                            gchar        **keys,
+                            gchar        **values,
+                            GError       **error)
+{
+        return real_save_png (pixbuf, keys, values, error,
+                              FALSE, f, NULL, NULL);
+}
+
+static gboolean
+gdk_pixbuf__png_image_save_to_callback (GdkPixbufSaveFunc   save_func,
+                                        gpointer            user_data,
+                                        GdkPixbuf          *pixbuf, 
+                                        gchar             **keys,
+                                        gchar             **values,
+                                        GError            **error)
+{
+        return real_save_png (pixbuf, keys, values, error,
+                              TRUE, NULL, save_func, user_data);
+}
+
 void
 MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
 {
@@ -909,6 +970,7 @@ MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
         module->stop_load = gdk_pixbuf__png_image_stop_load;
         module->load_increment = gdk_pixbuf__png_image_load_increment;
         module->save = gdk_pixbuf__png_image_save;
+        module->save_to_callback = gdk_pixbuf__png_image_save_to_callback;
 }
 
 void